home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #2 / Monster Media No. 2 (Monster Media)(1994).ISO / utils1 / 2m21src.zip / 2MFBOOT.ASM < prev    next >
Assembly Source File  |  1994-05-31  |  64KB  |  1,492 lines

  1.  
  2. ;┌───────────────────────────────────────────────────────────────────┐
  3. ;│                                                                   │
  4. ;│             █████ █   █ █▀▀▀▀ █▀▀▄  ▄▀▀▀▄ ▄▀▀▀▄ ▀▀█▀▀             │
  5. ;│                 █ ██ ██ █     █   █ █   █ █   █   █               │
  6. ;│             █████ █ █ █ █▀▀   █▀▀█  █   █ █   █   █               │
  7. ;│             █     █   █ █     █   █ █   █ █   █   █               │
  8. ;│             █████ █   █ █     █▄▄▀  ▀▄▄▄▀ ▀▄▄▄▀   █               │
  9. ;│                                                                   │
  10. ;│    2MFBOOT 2.1  -  (C) Mayo 1994  Ciriaco García de Celis.        │
  11. ;│                                                                   │
  12. ;│            CODIGO 2M PARA ARRANQUE FRIO DESDE DISQUETE.           │
  13. ;│                                                                   │
  14. ;│  Proceso:                                                         │
  15. ;│                                                                   │
  16. ;│    TASM    2MFBOOT /m5                                            │
  17. ;│    TLINK   2MFBOOT                                                │
  18. ;│    EXE2BIN 2MFBOOT.EXE 2MFBOOT.BIN                                │
  19. ;│                                                                   │
  20. ;│    El fichero .BIN hay que convertirlo a .DB con 2MFBMAKE.BAS     │
  21. ;│    Es necesario que este fichero ocupe exactamente 2560 bytes     │
  22. ;│                                                                   │
  23. ;└───────────────────────────────────────────────────────────────────┘
  24.  
  25.                .286                    ; versión para AT o superior
  26.  
  27. ; ------------ Macros de propósito general.
  28.  
  29. XPUSH          MACRO regmem            ; apilar lista de registros
  30.                  IRP rm, <regmem>
  31.                    PUSH rm
  32.                  ENDM
  33.                ENDM
  34.  
  35. XPOP           MACRO regmem            ; desapilar lista de registros
  36.                  IRP rm, <regmem>
  37.                    POP rm
  38.                  ENDM
  39.                ENDM
  40.  
  41. DELAY          MACRO                   ; estados de espera
  42.                  JMP SHORT $+2         ; para AT obsoleto
  43.                  JMP SHORT $+2
  44.                ENDM
  45.  
  46. PMICRO         MACRO
  47.                  CALL pmicro_iter      ; realmente es una subrutina
  48.                ENDM
  49.  
  50. ; ------------ Estructura de datos con información para cada unidad.
  51.  
  52. info_drv       STRUC
  53. maxs           EQU   13           ; máximo 13 sectores físicos/pista
  54. tipo_drv       DB    ?            ; tipo de la disquetera (0 = no hay)
  55. control2m_flag DB    OFF          ; a ON si 2M controla la unidad
  56. cambio         DB    ON           ; a ON indica cambio de soporte
  57. version_fmt    DB    ?            ; versión del formato de disco 2M
  58. multi_io       DB    ?            ; a 0 si posible acceso multi-sector
  59. chk            DB    ?            ; a 0 si checksum del sector 0 Ok
  60. vunidad        EQU   THIS WORD
  61. vunidad0       DB    ?            ; velocidad pista 0
  62. vunidadx       DB    ?            ; velocidad demás pistas
  63. gap            DB    ?            ; GAP entre sectores (leer/escribir)
  64. sectpista      DB    ?            ; sectores lógicos por pista
  65. tabla_tsect    DB    maxs DUP (?) ; tamaños de sectores 1, 2, ..., N
  66. tam_fat        DB    ?            ; sectores/FAT en la unidad
  67.                ENDS
  68.  
  69. ; ------------ Programa.
  70.  
  71. _PRINCIPAL     SEGMENT
  72.                ASSUME CS:_PRINCIPAL, DS:_PRINCIPAL
  73.  
  74.                ORG   0                 ; código binario puro
  75.  
  76. ; ****************************************
  77. ; *                                      *
  78. ; *   D A T O S    R E S I D E N T E S   *
  79. ; *                                      *
  80. ; ****************************************
  81.  
  82. ; ------------ Variables del programa (justo al principio).
  83.  
  84. info_ptr       DW    info_A       ; punteros a datos de las unidades
  85.                DW    info_B
  86.  
  87.                DB    "21"         ; Versión 2MFBOOT 2.1
  88.  
  89. id_sistema     DB    "2M-STV"     ; identificación de disco 2M
  90. unidad         DB    ?            ; unidad física de disco en curso
  91. numsect        DW    ?            ; sectores a transferir
  92. sectini        DW    ?            ; primer sector DOS a transferir
  93. cilindro       DB    ?            ; cilindro del disco a acceder
  94. cabezal        DB    ?            ; cabezal a emplear
  95. sector         DB    ?            ; número de sector físico
  96. sector_ini     DB    ?            ; número de sector físico inicial
  97. sector_fin     DB    ?            ; número de sector físico final
  98. seccion        DB    ?            ; parte del sector físico en curso
  99. secciones      DB    ?            ; sectores lógicos a transferir
  100. tsector        DB    ?            ; LOG2 (tamaño de sector) - 7
  101. buffer         DW    buffer_io    ; puntero al buffer intermedio
  102. buf_unidad     DB    ?            ; unidad del sector en el buffer
  103. buf_cilcab     DW    ?            ; cilindro/cabezal de sector buffer
  104. buf_sector     DB    ?            ; número de sector en el buffer
  105. status         DB    ?            ; resultado de los accesos a disco
  106. fdc_result     DB    7 DUP (?)    ; bytes de resultados del FDC
  107. orden          DB    ?            ; operación F_READ/F_WRITE/F_VERIFY
  108. tab_ordenes    DB    F_READ
  109.                DB    F_WRITE
  110.                DB    F_VERIFY     ; órdenes 2, 3 y 4
  111.  
  112.                ; --- Interpretación BIOS de los bits de ST1
  113.  
  114. lista_errs     DB    4            ; 'sector not found'
  115.                DB    0
  116.                DB    10h          ; 'bad CRC'
  117.                DB    8            ; 'DMA overrun'
  118.                DB    0
  119.                DB    4            ; 'sector not found'
  120.                DB    3            ; 'write-protect error'
  121.                DB    2            ; 'address mark not found'
  122.                DB    20h          ; en otro caso: 'bad NEC'
  123.  
  124. info_A         info_drv <>        ; datos de A:
  125. info_B         info_drv <>        ; datos de B:
  126.  
  127. ; ***************************************
  128. ; *                                     *
  129. ; *   C O D I G O   R E S I D E N T E   *
  130. ; *                                     *
  131. ; ***************************************
  132.  
  133. ; ------------ Nueva rutina de gestión de INT 13h. Llama a la INT 13h
  134. ;              original o a una nueva rutina de control para la
  135. ;              lectura (AH=2), escritura (AH=3) y verificación (AH=4)
  136. ;              según el tipo de disco introducido.
  137.  
  138. ges_int13      PROC  FAR
  139.                STI
  140.                CLD
  141.                PUSHF
  142.                PUSH  SI
  143.                CMP   DL,2
  144.                JAE   ges13bios         ; no es disquetera A: ó B:
  145.                CALL  set_SI_drv
  146.                CMP   CS:[SI].tipo_drv,2  ; ¿unidad 1.2M?
  147.                JE    ges_2m
  148.                CMP   CS:[SI].tipo_drv,4  ; ¿unidad 1.44/2.88M?
  149. ges_2m:        JC    ges13bios         ; no es unidad de alta densidad
  150.                CMP   AH,2
  151.                JB    ges13bios         ; no Read/Write/Verify/Format
  152.                CMP   AH,5
  153.                JA    ges13bios         ; no Read/Write/Verify/Format
  154.                JNE   no_format
  155.                CALL  set_flag_STV      ; CF = 0 -> "disco no 2M"
  156.                JMP   ges13bios
  157. no_format:     CALL  detecta_cambio    ; ¿cambio de disco?
  158.                JNC   dilucida
  159.                POP   SI
  160.                POPF
  161.                STC                     ; hubo cambio:
  162.                MOV   AX,600h
  163.                RET   2                 ; retornar con error
  164. dilucida:      CMP   CS:[SI].control2m_flag,OFF
  165.                JE    ges13bios         ; la unidad la controla la BIOS
  166.                POP   SI
  167.                POPF
  168.                CALL  control2m         ; la controla 2M
  169.                RET   2
  170. ges13bios:     POP   SI
  171.                POPF
  172.                JMP   CS:ant_int13      ; saltar al gestor de INT 13h
  173. ges_int13      ENDP
  174.  
  175. ; ------------ A la entrada en DL se indica la unidad y a la salida se
  176. ;              devuelve SI apuntando sus variables sin alterar flags.
  177.  
  178. set_SI_drv     PROC
  179.                PUSHF
  180.                PUSH  BX
  181.                MOV   BL,DL
  182.                MOV   BH,0
  183.                SHL   BX,1
  184.                MOV   SI,CS:[BX+OFFSET info_ptr]
  185.                POP   BX
  186.                POPF
  187.                RET
  188. set_SI_drv     ENDP
  189.  
  190. ; ------------ Si CF=1, indicar disquete 2M presente. A la
  191. ;              entrada, DL indica la unidad de disco.
  192.  
  193. set_flag_STV   PROC
  194.                PUSHA
  195.                CALL  set_SI_drv
  196.                MOV   AL,ON                       ; indicar 2M
  197.                JC    tipo_stv_ok
  198.                MOV   AL,OFF                      ; indicar no 2M
  199. tipo_stv_ok:   MOV   CS:[SI].control2m_flag,AL
  200.                POPA
  201.                RET
  202. set_flag_STV   ENDP
  203.  
  204. ; ------------ Devolver ZF=1 si cilindro y cabezal 0.
  205.  
  206. pista0?        PROC
  207.                PUSH  AX
  208.                MOV   AL,cabezal
  209.                OR    AL,cilindro
  210.                POP   AX
  211.                RET
  212. pista0?        ENDP
  213.  
  214. ; ------------ Devolver ZF=1 si la línea de cambio de disco está
  215. ;              inactiva. A la entrada, DL contiene la unidad. El
  216. ;              motor es puesto en marcha y, si no lo estaba ya, la
  217. ;              variable que indica lo que resta para detenerlo
  218. ;              es llevada a su valor normal, por lo que el disco no
  219. ;              tardará mucho en detenerse (incluso sin quizá haber
  220. ;              acelerado aún). En la práctica, invocando esta rutina
  221. ;              desde INT 13h nunca será necesario arrancar el motor
  222. ;              ya que el DOS ejecuta antes la función equivalente,
  223. ;              la 16h, que lo pone en marcha. Es simplemente una
  224. ;              medida de seguridad contra las BIOS «de marca».
  225.  
  226. leer_lin_camb  PROC
  227.                PUSHA                   ; *
  228.                PUSH  DS
  229.                PUSH  40h
  230.                POP   DS
  231.                MOV   AL,1
  232.                MOV   CL,DL
  233.                SHL   AL,CL             ; bit de motor en 0..3
  234.                TEST  DS:[3Fh],AL
  235.                JNZ   rodando           ; el motor ya está girando
  236.                CLC
  237.                CALL  motor_off_cnt     ; cuenta normal detención motor
  238. rodando:       MOV   AH,DL
  239.                SHL   AH,4
  240.                OR    AH,AL             ; AH = byte BIOS
  241.                SHL   AL,4
  242.                OR    AL,00001100b      ; modo DMA, no hacer reset
  243.                OR    AL,DL             ; AL para reg. salida digital
  244.                MOV   DX,3F2h
  245.                CLI
  246.                MOV   DS:[3Fh],AH       ; actualizar variable BIOS
  247.                OUT   DX,AL             ; arrancado motor en la unidad
  248.                ADD   DX,5
  249.                DELAY
  250.                IN    AL,DX             ; leer línea de cambio de disco
  251.                STI
  252.                TEST  AL,80h            ; ZF=0 -> cambio de disco
  253.                POP   DS
  254.                POPA                    ; *
  255.                RET
  256. leer_lin_camb  ENDP
  257.  
  258. ; ------------ Determinar si ha habido cambio de disco y, en ese caso,
  259. ;              si el nuevo disquete es de tipo 2M o no. El cambio de
  260. ;              disco se detecta leyendo la línea de cambio de disco o
  261. ;              chequeando la variable que indica si ha habido cambio
  262. ;              o no (esta variable está a ON tras instalar 2M para
  263. ;              forzar la detección del tipo de disco introducido; se
  264. ;              pone en ON también si no se logra bajar la línea de
  265. ;              cambio de disco por si fuera un soporte raro y la BIOS
  266. ;              sí lo lograra -forzando así una detección posterior-).
  267.  
  268. detecta_cambio PROC
  269.                PUSHA                   ; *
  270.                CALL  set_SI_drv        ; SI -> variables de la unidad
  271.                CMP   CS:[SI].cambio,ON ; ¿cambio de soporte?
  272.                MOV   CS:[SI].cambio,OFF
  273.                JE    hubo_cambio
  274.                CALL  leer_lin_camb     ; leer línea de cambio de disco
  275.                JNZ   hubo_cambio
  276.                POPA
  277.                CLC                     ; no hay cambio de disco
  278.                RET
  279. hubo_cambio:   CLC
  280.                CALL  set_flag_STV      ; CF = 0 -> supuesto no 2M
  281.                XPUSH <DS, ES>          ; **
  282.                MOV   BX,90h
  283.                ADD   BL,DL
  284.                PUSH  40h
  285.                POP   DS
  286.                AND   BYTE PTR [BX],255-16 ; densidad no determinada
  287.                XPUSH <CS, CS>
  288.                XPOP  <DS, ES>
  289.                MOV   unidad,DL
  290.                STC                     ; asegurar motor en marcha
  291.                CALL  reset_drv
  292.                MOV   cilindro,1
  293.                MOV   cabezal,0
  294.                CALL  seek_drv          ; bajar línea cambio de disco
  295.                DEC   cilindro
  296.                CALL  seek_drv
  297.                CLC
  298.                CALL  motor_off_cnt     ; cuenta normal detención motor
  299.                CALL  leer_lin_camb     ; ¿bajada línea cambio disco?
  300.                JZ    disco_dentro      ; se pudo: hay disco dentro
  301.                MOV   [SI].cambio,ON    ; futura detección tipo disco
  302.                CLC                     ; NO indicar cambio de disco...
  303.                JMP   fin_detecta       ; ...para pasar control a BIOS
  304. disco_dentro:  PUSH  DS
  305.                PUSH  40h
  306.                POP   DS
  307.                MOV   BYTE PTR DS:[41h],6  ; error 'media changed'
  308.                POP   DS
  309.                MOV   buf_unidad,-1     ; invalidar buffer
  310.                MOV   [SI].gap,20       ; GAP provisional
  311.                MOV   CX,3              ; 3 intentos
  312. intenta_io0:   PUSH  CX
  313.                CMP   CX,2              ; CF=1 la 3ª vez (a 0 si CX<>1)
  314.                CALL  reset_drv
  315.                MOV   [SI].vunidad0,0   ; empezar con 500 Kbit/seg.
  316. intenta_io:    MOV   cilindro,0
  317.                MOV   cabezal,0
  318.                MOV   sector,1          ; sector de arranque
  319.                MOV   seccion,0
  320.                MOV   secciones,1
  321.                MOV   orden,F_READ
  322.                MOV   DI,buffer
  323.                CALL  direct_acceso
  324.                JNE   otra_densidad     ; es otra densidad de disco
  325.                POP   CX
  326.                MOV   BX,buffer
  327.                CALL  set_info          ; características nuevo soporte
  328.                CLC
  329.                JMP   fin_detecta_c     ; indicar cambio de disco
  330. otra_densidad: MOV   AL,[SI].vunidad0
  331.                INC   AX                ; próxima velocidad
  332.                CMP   AL,3
  333.                JA    otro_intento
  334.                MOV   [SI].vunidad0,AL
  335.                JMP   intenta_io        ; probar otra velocidad
  336. otro_intento:  MOV   [SI].vunidad0,0
  337.                POP   CX
  338.                LOOP  intenta_io0       ; reintento
  339. fin_detecta_c: STC                     ; indicar cambio de disco
  340. fin_detecta:   XPOP  <ES, DS>          ; **
  341.                POPA                    ; *
  342.                RET
  343. detecta_cambio ENDP
  344.  
  345. ; ------------ Anotar la información del disquete si es de tipo 2M.
  346. ;              A la entrada, DS:SI apunta a las variables de la unidad
  347. ;              y ES:BX al sector de arranque del disco. Se actualiza
  348. ;              también la variable BIOS de tipo de densidad (la BIOS
  349. ;              no se da cuenta del cambio de disco y conviene ayudar).
  350.  
  351. set_info       PROC
  352.                PUSHA
  353.                CALL  calc_chk
  354.                JC    set_info_exit     ; no es disco 2M
  355.                MOV   [SI].chk,AL         ; anotar checksum
  356.                MOV   [SI].version_fmt,CL ; y versión del formato
  357.                MOV   DL,unidad
  358.                STC
  359.                CALL  set_flag_STV      ; CF = 1 -> indicar disco 2M
  360.                MOV   AL,ES:[BX+22]     ; tamaño de FAT
  361.                MOV   [SI].tam_fat,AL
  362.                MOV   CL,ES:[BX+65]     ; CL a 0 si acceso multi-sector
  363.                MOV   [SI].multi_io,CL
  364.                MOV   AX,ES:[BX+66]
  365.                MOV   [SI].vunidad,AX   ; velocidad pista 0 / demás
  366.                MOV   AL,ES:[BX+24]
  367.                MOV   [SI].sectpista,AL ; sectores/pista
  368.                MOV   DI,ES:[BX+72]
  369.                MOV   AL,ES:[BX+DI+1]   ; GAP de formateo
  370.                MOV   AH,AL
  371.                AND   CL,CL             ; CL a 0 si acceso multi-sector
  372.                JZ    gap_rw_ok         ; GAP R/W para /F
  373.                ADD   AH,190
  374.                MOV   AL,11
  375.                MUL   AH                ; AX = (190+GAP)*11
  376.                SUB   AX,2048+62
  377. gap_rw_ok:     SHR   AL,1              ; GAP R/W para /M
  378.                MOV   [SI].gap,AL
  379.                MOV   CX,maxs
  380.                MOV   DI,ES:[BX+74]
  381.                ADD   DI,BX
  382.                LEA   BX,[SI].tabla_tsect
  383. genera_ts:     MOV   AL,ES:[DI]
  384.                MOV   [BX],AL
  385.                INC   BX
  386.                INC   DI
  387.                LOOP  genera_ts         ; información estructura pistas
  388. set_info_exit: MOV   AL,[SI].vunidad0
  389.                SHL   AL,6              ; velocidad en bits 7:6
  390.                OR    AL,00010111b      ; establecido otro medio físico
  391.                CMP   [SI].tipo_drv,2
  392.                JA    modo_ok           ; es unidad de 3½
  393.                AND   AL,11111000b
  394.                OR    AL,00000101b      ; 1.2 en 1.2
  395.                TEST  AL,01000000b
  396.                JZ    modo_ok
  397.                XOR   AL,00100001b      ; 360 en 1.2 y seek * 2
  398. modo_ok:       PUSH  DS
  399.                MOV   BX,90h
  400.                ADD   BL,unidad
  401.                PUSH  40h
  402.                POP   DS
  403.                AND   BYTE PTR DS:[BX],8  ; respetar bit de 2.88M
  404.                OR    DS:[BX],AL        ; actualizar variable BIOS
  405.                POP   DS                ; con el tipo de densidad
  406.                POPA
  407.                RET
  408. set_info       ENDP
  409.  
  410. ; ------------ Calcular el checksum de la zona vital del sector de
  411. ;              arranque. A la entrada, ES:BX -> sector de arranque.
  412. ;              A la salida, CF=1 si el disco no es 2M; de otro modo
  413. ;              checksum en AL y versión del formato de disco en CL.
  414.  
  415. calc_chk       PROC
  416.                XPUSH <SI, DI>
  417.                LEA   DI,[BX+3]         ; DI=BX+3
  418.                LEA   SI,id_sistema
  419.                MOV   CX,6
  420.                REP   CMPSB             ; comparar identificación
  421.                STC
  422.                JNE   chk_ret           ; el disco no es 2M
  423.                XOR   AX,AX
  424.                MOV   CL,ES:[BX+64]     ; versión del formateador
  425.                CMP   CL,6
  426.                JB    chk_ok            ; no usaba este checksum
  427.                MOV   DI,ES:[BX+68]
  428. chk_sum:       DEC   DI
  429.                ADD   AL,ES:[BX+DI]
  430.                CMP   DI,63
  431.                JA    chk_sum
  432. chk_ok:        CLC
  433. chk_ret:       XPOP  <DI, SI>
  434.                RET
  435. calc_chk       ENDP
  436.  
  437. ; ------------ Determinar el tipo de error producido en el acceso.
  438.  
  439. set_err        PROC
  440.                PUSHA
  441.                JNC   err_ret           ; no hay error
  442.                CMP   status,0          ; ¿'status' ya asignado?
  443.                JNE   err_retc          ; no cambiarlo si es así
  444.                MOV   AL,BYTE PTR fdc_result+1
  445.                AND   AL,10110111b      ; aislar condiciones de test
  446.                LEA   BX,lista_errs
  447.                MOV   CX,9
  448. busca_err:     MOV   AH,[BX]           ; código de error BIOS
  449.                SHL   AL,1
  450.                JC    err_ok            ; es ese error
  451.                INC   BX
  452.                LOOP  busca_err         ; buscar otro error
  453. err_ok:        OR    status,AH
  454. err_retc:      STC                     ; condición de error
  455. err_ret:       POPA
  456.                RET
  457. set_err        ENDP
  458.  
  459. ; ------------ Actualizar variables de error de la BIOS.
  460.  
  461. set_bios_err   PROC
  462.                PUSHF                   ; *
  463.                PUSHA                   ; **
  464.                PUSH  ES                ; ***
  465.                PUSH  40h
  466.                POP   ES
  467.                MOV   DI,41h            ; bytes de resultados del 765
  468.                LEA   SI,status         ; variable BIOS de status y 7
  469.                MOV   CX,4              ; bytes: 4 palabras
  470.                REP   MOVSW
  471.                POP   ES                ; ***
  472.                POPA                    ; **
  473.                POPF                    ; *
  474.                RET
  475. set_bios_err   ENDP
  476.  
  477. ; ------------ Realizar lecturas, escrituras y verificaciones: rutina
  478. ;              que sustituye el código de la BIOS para poder soportar
  479. ;              los formatos 2M. La operación puede quedar dividida en
  480. ;              tres fases: el fragmento anterior a la FAT2, la zona
  481. ;              correspondiente a la FAT2 (se ignora la escritura y se
  482. ;              simula su lectura leyendo la FAT1) y un último bloque
  483. ;              ubicado tras la FAT2. El sector de arranque es emulado
  484. ;              empleando el primer sector físico de la FAT2 (aunque en
  485. ;              los discos de versión de formato anterior a la 7 se usa
  486. ;              el sector de arranque verdadero -permitiendo escribirlo
  487. ;              sólo si es válido-). En cualquier caso, si el número de
  488. ;              cabezal tiene el bit 7 activo, se sobreentiende que el
  489. ;              programa que llama soporta disquetes 2M y no se emula
  490. ;              la FAT2 ni el sector de arranque, para permitirle
  491. ;              acceder al código SuperBOOT. Las coordenadas de la BIOS
  492. ;              se traducen a las unidades del DOS por mayor comodidad.
  493.  
  494. control2m      PROC
  495.                PUSH  DS                ; *
  496.                PUSHA                   ; **
  497.                PUSH  CS
  498.                POP   DS
  499.                MOV   unidad,DL
  500.                CALL  set_SI_drv        ; SI -> variables de la unidad
  501.                CMP   [SI].chk,0
  502.                JE    chk_valido        ; checksum correcto en sector 0
  503.                MOV   status,40h        ; devolver 'Seek Error' al DOS
  504.                JMP   exit_2m_ctrl
  505. chk_valido:    PUSH  AX                ; ***
  506.                MOV   AH,0
  507.                MOV   numsect,AX        ; nº sectores
  508.                MOV   AL,CH             ; cilindro
  509.                SHL   AL,1
  510.                MOV   DL,DH
  511.                AND   DH,01111111b
  512.                ADD   AL,DH             ; cabezal físico
  513.                MUL   [SI].sectpista
  514.                ADD   AL,CL             ; sector
  515.                ADC   AH,0
  516.                DEC   AX                ; AX = nº sector DOS
  517.                MOV   sectini,AX
  518.                MOV   DI,BX             ; ES:DI -> dirección
  519.                POP   BX                ; ***
  520.                MOV   BL,BH
  521.                MOV   BH,0
  522.                MOV   CL,[BX+OFFSET tab_ordenes-2]
  523.                MOV   orden,CL
  524.                SHL   DL,1
  525.                JC    acceso_final      ; cabezal >= 128: no emular
  526.                AND   AX,AX             ; ¿comienza en sector 0?
  527.                JNZ   io_emula          ; no
  528.                CMP   [SI].version_fmt,7
  529.                JB    boot_real         ; no soportado BOOT virtual
  530.                MOV   AL,[SI].tam_fat   ; AH = 0
  531.                INC   AX
  532.                MOV   CX,1              ; sector BOOT emulado en
  533.                CALL  ejecuta_io        ; el primer sector FAT2
  534.                JNE   fin_ctrl
  535. boot_fin_op:   DEC   numsect
  536.                INC   sectini
  537.                MOV   AX,sectini
  538.                JMP   io_emula
  539. boot_real:     CMP   orden,F_WRITE
  540.                JNE   io_emula
  541.                MOV   BX,DI             ; BOOT de 2M 1.3 y anteriores
  542.                CALL  calc_chk
  543.                JC    si_skip           ; no es de tipo 2M
  544.                AND   AL,AL
  545.                JZ    io_emula          ; lo es y con checksum correcto
  546. si_skip:       ADD   DI,512
  547.                JMP   boot_fin_op       ; impedir estropicio de BOOT
  548. io_emula:      MOV   CL,[SI].tam_fat
  549.                MOV   CH,0              ; CX = primer sector FAT2 - 1
  550.                CMP   AX,CX
  551.                JA    en_fat2?          ; ¿la operación afecta a FAT2?
  552.                CALL  calc_iop          ; calcular sectores antes FAT2
  553.                CALL  ejecuta_io        ; CX sectores desde AX
  554.                JNE   fin_ctrl          ; error
  555.                CMP   numsect,0
  556.                JE    fin_ctrl          ; fin de la transferencia
  557. en_fat2?:      MOV   AX,sectini
  558.                MOV   CL,[SI].tam_fat
  559.                MOV   CH,0
  560.                SHL   CX,1              ; CX = último sector FAT2
  561.                CMP   AX,CX
  562.                JA    acceso_final      ; la operación es tras la FAT2
  563.                CALL  calc_iop          ; sectores hasta fin de FAT2
  564.                CMP   orden,F_WRITE
  565.                JNE   emula_fat1
  566.                SHL   CX,9              ; CX = CX * 512
  567.                ADD   DI,CX             ; ES:DI actualizado
  568.                JMP   acceso_final
  569. emula_fat1:    MOV   DL,[SI].tam_fat
  570.                MOV   DH,0
  571.                SUB   AX,DX             ; leer de FAT1 y no de la FAT2
  572.                CALL  ejecuta_io        ; CX sectores desde AX
  573.                JNE   fin_ctrl          ; error
  574. acceso_final:  CMP   numsect,0
  575.                JE    fin_ctrl          ; fin de la transferencia
  576.                MOV   AX,sectini
  577.                MOV   CX,numsect
  578.                CALL  ejecuta_io
  579. fin_ctrl:      CLC
  580.                CALL  motor_off_cnt     ; cuenta normal detención motor
  581.                CALL  set_bios_err      ; actualizar variables BIOS
  582. exit_2m_ctrl:  POPA                    ; **
  583.                MOV   AH,status
  584.                POP   DS                ; *
  585.                AND   AH,AH
  586.                JZ    st_ok             ; resultado correcto (CF=0)
  587.                STC                     ; error
  588.                MOV   AL,0              ; 0 sectores movidos
  589. st_ok:         RET
  590. calc_iop:      SUB   CX,AX
  591.                INC   CX                ; CX sectores
  592.                CMP   CX,numsect
  593.                JBE   nsect_ok
  594.                MOV   CX,numsect        ; sólo quedan CX
  595. nsect_ok:      SUB   numsect,CX
  596.                ADD   sectini,CX
  597.                RET
  598. control2m      ENDP
  599.  
  600. ; ------------ A la entrada, AX indica el sector inicial (coordenadas
  601. ;              del DOS) y CX el número de sectores a procesar.
  602. ;              * Definiciones: «Sector físico» es un sector del disco
  603. ;              de 512, 1024 ó 2048 bytes (números de sector del 1 al N
  604. ;              en la pista). Este sector físico está dividido en
  605. ;              «secciones» de 512 bytes, constando por tanto de 1, 2 ó
  606. ;              4 secciones. «Sector virtual» es el número de sector
  607. ;              del programa que llama a INT 13h, comprendido entre 1 y
  608. ;              M. Esta estructura de N sectores por pista de distintos
  609. ;              tamaños, se verifica en todo el disco con excepción del
  610. ;              cabezal y cilindro 0 (con un formato más convencional
  611. ;              de sectores de 512 bytes numerados de 1 a J, aunque no
  612. ;              existen algunos de los intermedios que corresponden a
  613. ;              la segunda copia de la FAT).
  614. ;              * Primero se convierte el sector virtual (1..M) en su
  615. ;              correspondiente físico (1..J en la pista 0 y 1..N en
  616. ;              las demás), deduciendo qué porción de 512 bytes (o
  617. ;              sección) es afectada. Un sector virtual (512 bytes)
  618. ;              simulado suele ser parte de un sector físico de 2048
  619. ;              bytes en muchos casos. Si dicho sector físico ya había
  620. ;              sido leído al buffer en anteriores accesos, se extrae
  621. ;              la sección necesaria. Si no, se carga del disco y se
  622. ;              extrae dicho fragmento. El número de sectores virtuales
  623. ;              que se solicitan (=secciones) permite realizar un bucle
  624. ;              hasta completar la transferencia; el interleave 1:2 de
  625. ;              los sectores físicos en /M permite acceder sector a
  626. ;              sector sin pérdida de rendimiento. En el caso de la
  627. ;              escritura, se estudia primero si hay varios sectores
  628. ;              virtuales consecutivos que escribir, completando entre
  629. ;              todos un sector físico: en ese caso, se prepara el
  630. ;              mismo y se escribe sin más. En caso de que haya que
  631. ;              modificar sólo una única sección de un sector físico,
  632. ;              salvo si éste es de 512 bytes, no hay más remedio que
  633. ;              cargarlo al buffer (realizar una prelectura),
  634. ;              actualizar la sección correspondiente y volverlo a
  635. ;              escribir.
  636. ;              * En el formato /F se realiza una operación multisector
  637. ;              si es posible y sin emplear el buffer intermedio (si
  638. ;              bien podría ser preciso emplearlo con la primera y
  639. ;              última sección); en los dos formatos de disco se hace
  640. ;              la operación multisector en la primera pista. Las
  641. ;              operaciones multisector puede que sea preciso
  642. ;              dividirlas en tres fases: los sectores antes de una
  643. ;              frontera de DMA, el que la cruza (que es transferido
  644. ;              a través del buffer intermedio) y los que están detrás.
  645.  
  646. ejecuta_io     PROC
  647.                MOV   BX,AX             ; AX = sector DOS inicial
  648.                MOV   secciones,CL      ; CX sectores (CL realmente)
  649.                DIV   [SI].sectpista
  650.                INC   AH                ; numerado desde 1...
  651.                MOV   sector,AH         ; ...el resto es el sector
  652.                SHR   AL,1
  653.                MOV   cilindro,AL       ; cilindro
  654.                RCL   AL,1
  655.                AND   AL,1
  656.                MOV   cabezal,AL        ; cabezal
  657.                MOV   AL,sector
  658.                ADD   AL,secciones
  659.                JC    no_cabe           ; sector+secciones > 255
  660.                DEC   AX                ; DEC AX = DEC AL
  661.                CMP   AL,[SI].sectpista
  662.                JBE   si_cabe
  663. no_cabe:       MOV   status,4          ; 'sector no encontrado'
  664.                JMP   fin_io
  665. si_cabe:       MOV   AL,AH             ; sector en AL
  666.                CBW                     ; sección 0 (AH = 0)
  667.                CALL  pista0?
  668.                JZ    s_xx              ; sector físico en pista/cara 0
  669.                LEA   BX,[SI].tabla_tsect-1
  670.                DEC   AX                ; AH = 0
  671. resta_secc:    INC   BX
  672.                INC   AH
  673.                MOV   CL,[BX]
  674.                SUB   CL,2
  675.                MOV   CH,1
  676.                SHL   CH,CL
  677.                SUB   AL,CH
  678.                JNC   resta_secc        ; en las demás pistas
  679.                ADD   AL,CH
  680.                XCHG  AH,AL
  681. s_xx:          MOV   sector,AL         ; sector lógico convertido a
  682.                MOV   seccion,AH        ; sector y sección físicas
  683. direct_acceso: CALL  motor_ok          ; asegurar que está en marcha
  684.                MOV   AH,0
  685.                MOV   sector_fin,AH     ; no acceder a más de 1 sector
  686.                CALL  pista0?           ; (al menos de momento)
  687.                JNZ   decide_multi      ; no es pista 0
  688.                MOV   AL,secciones
  689.                MOV   secciones,AH      ; las que restan (AH = 0)
  690.                JMP   multi_proc
  691. decide_multi:  CMP   [SI].multi_io,AH  ; AH = 0
  692.                JNE   io_pasos          ; acceso sector a sector
  693.                CMP   seccion,AH
  694.                JE    multi_acc
  695.                CALL  acceso_secc       ; no acceso a inicio sector
  696.                JC    fin_io
  697. multi_acc:     CMP   secciones,AH      ; AH = 0
  698.                JE    fin_io
  699.                CALL  num_secciones
  700.                MOV   CL,AL
  701.                MOV   AL,secciones      ; AH = 0
  702.                DIV   CL
  703.                AND   AL,AL
  704.                JZ    io_pasos          ; no quedan sectores enteros
  705.                MOV   secciones,AH      ; las que restan
  706. multi_proc:    CALL  acceso_multi      ; de AL sectores
  707.                JC    fin_io
  708. io_pasos:      CMP   secciones,0
  709.                JE    fin_io            ; no restan secciones finales
  710.                CALL  acceso_secc
  711.                JNC   io_pasos
  712. fin_io:        CMP   status,0          ; ZF = 1 -> operación correcta
  713.                RET
  714.  
  715. acceso_secc:   PUSH  AX
  716.                CMP   orden,F_WRITE     ; acabar transferencia sector
  717.                JE    escritura
  718.                CMP   orden,F_VERIFY
  719.                JE    verificacion
  720.                CALL  leido?            ; realizar lectura...
  721.                JNC   ya_leido          ; sector ya en el buffer
  722. hay_que_leer:  CALL  acceso_sector     ; efectuar E/S
  723.                JC    acc_ret           ; ha habido fallo
  724. ya_leido:      CALL  trans_secc        ; buffer -> memoria
  725.                JMP   acc_ret
  726. escritura:     CMP   seccion,0
  727.                JNE   prelectura        ; sólo parte del sector cambia
  728.                CALL  num_secciones
  729.                CMP   secciones,AL
  730.                JAE   escribir          ; Todo el sector físico cambia
  731. prelectura:    CALL  leido?            ; Leer el sector físico para
  732.                JNC   escribir          ; cambiar sólo una parte de él
  733.                MOV   orden,F_READ      ; de momento leer...
  734.                CALL  acceso_sector     ; efectuar E/S
  735.                MOV   orden,F_WRITE     ; ... restaurar orden original
  736.                JC    acc_ret           ; ha habido fallo
  737. escribir:      CALL  trans_secc        ; memoria -> buffer
  738.                CALL  acceso_sector     ; volcar buffer al disco
  739.                JMP   acc_ret
  740. verificacion:  PUSH  BX
  741.                MOV   BL,seccion
  742.                CALL  num_secciones
  743. dec_sec_veri:  DEC   secciones
  744.                JZ    verifica
  745.                INC   BX
  746.                CMP   BL,AL
  747.                JB    dec_sec_veri
  748. verifica:      POP   BX
  749.                CALL  acceso_sector     ; leer para forzar verificación
  750. acc_ret:       PUSHF
  751.                INC   sector            ; preparado para otro sector
  752.                MOV   seccion,0         ; desde su primera sección
  753.                POPF
  754.                POP   AX
  755.                RET
  756.  
  757. acceso_multi:  PUSH  AX                ; AL = sectores a transferir
  758.                AND   AL,AL
  759.                JZ    acc_mult_fin
  760.                MOV   AH,sector
  761.                MOV   sector_ini,AH
  762.                ADD   AL,AH
  763.                DEC   AX
  764.                MOV   sector_fin,AL
  765.                INC   AL
  766.                CALL  acceso_sector     ; sectores no problemáticos
  767.                MOV   sector,AL
  768. acc_mult_fin:  POP   AX
  769.                RET
  770. ejecuta_io     ENDP
  771.  
  772. ; ------------ Mover secciones desde el buffer hacia la memoria (con
  773. ;              orden F_READ) después de la lectura o de la memoria al
  774. ;              buffer (orden F_WRITE) antes de la escritura. En la
  775. ;              verificación (orden F_VERIFY) no se mueve nada porque
  776. ;              esta subrutina no es invocada.
  777.  
  778. trans_secc     PROC
  779.                XPUSH <AX, BX, CX, SI>  ; *
  780.                MOV   BL,seccion        ; desde esta sección
  781.                CALL  num_secciones     ; nº secciones del sector
  782. otra_secci:    PUSH  BX
  783.                SHL   BX,9              ; sección * 512
  784.                ADD   BX,buffer         ; dirección
  785.                MOV   SI,BX
  786.                MOV   CX,256            ; tamaño sección (palabras)
  787.                CALL  swap_reg          ; ¿intercambiar origen-destino?
  788.                REP   MOVSW             ; copiar 512 bytes
  789.                CALL  swap_reg          ; ¿intercambiar origen-destino?
  790.                POP   BX
  791.                DEC   secciones         ; una menos
  792.                JZ    fin_secc
  793.                INC   BX                ; otra sección del sector
  794.                CMP   BL,AL             ; ¿sector agotado?
  795.                JB    otra_secci        ; aún no
  796. fin_secc:      XPOP  <SI, CX, BX, AX>  ; *
  797.                RET
  798. swap_reg:      CMP   CS:orden,F_WRITE
  799.                JE    interc
  800.                CLC
  801.                RET
  802. interc:        XCHG  SI,DI             ; en escritura, invertir el
  803.                XPUSH <ES, DS>          ; sentido de la operación
  804.                XPOP  <ES, DS>
  805.                RET
  806. trans_secc     ENDP
  807.  
  808. ; ------------ Comprobar si el sector ya está en el buffer.
  809.  
  810. leido?         PROC
  811.                PUSH  AX
  812.                MOV   AL,buf_unidad
  813.                CMP   AL,unidad
  814.                JNE   no_leido          ; es en otra unidad
  815.                MOV   AL,cilindro
  816.                MOV   AH,cabezal
  817.                CMP   AX,buf_cilcab
  818.                JNE   no_leido          ; es en otro cilindro/cabezal
  819.                MOV   AL,buf_sector
  820.                CMP   AL,sector
  821.                JNE   no_leido          ; es otro sector
  822.                POP   AX
  823.                RET                     ; está en el buffer
  824. no_leido:      STC
  825.                POP   AX
  826.                RET                     ; sector no leído
  827. leido?         ENDP
  828.  
  829. ; ------------ Leer o escribir sector(es). Se selecciona el tamaño de
  830. ;              sector correcto antes de llamar a sector_io.  En esta
  831. ;              rutina se actualiza la variable «status» en función de
  832. ;              los posibles errores de acceso.  Si sector_fin es
  833. ;              distinto de 0 se accede a los sectores indicados, si es
  834. ;              0 se accede sólo al sector «sector» a través del buffer
  835. ;              intermedio y al final se anota el sector cargado ó
  836. ;              escrito para evitar futuras lecturas innecesarias, a
  837. ;              modo de mini-caché que dispara la velocidad de acceso a
  838. ;              sectores lógicos consecutivos.
  839.  
  840. acceso_sector  PROC
  841.                XPUSH <AX, BX>
  842.                CALL  seek_drv          ; posicionar el cabezal
  843.                JNC   en_pista
  844.                CMP   status,0          ; ¿error ya determinado?
  845.                JNE   acc_fin_err
  846.                OR    status,40h        ; no: pues 'seek error'
  847. acc_fin_err:   STC
  848.                JMP   acceso_fin
  849. en_pista:      CALL  pista0?
  850.                MOV   AL,2
  851.                JZ    tam_acc_ok        ; sectores 512 en cil./cab. 0
  852.                LEA   BX,[SI].tabla_tsect
  853.                ADD   BL,sector
  854.                ADC   BH,0
  855.                MOV   AL,[BX-1]
  856. tam_acc_ok:    MOV   tsector,AL
  857.                CMP   sector_fin,0      ; ¿usar buffer intermedio?
  858.                JE    acceso_buffer
  859.                CALL  sector_io
  860.                MOV   sector_fin,0      ; no acceder a más de 1 sector
  861.                PUSHF                   ; **1
  862.                JMP   acceso_rep        ; en el futuro (por defecto)
  863. acceso_buffer: XPUSH <ES, DI>
  864.                PUSH  CS
  865.                POP   ES
  866.                MOV   DI,buffer         ; acceso con buffer auxiliar
  867.                MOV   AL,sector         ; mismo sector inicial/final
  868.                MOV   sector_ini,AL
  869.                MOV   sector_fin,AL
  870.                CALL  sector_io
  871.                MOV   sector_fin,0
  872.                XPOP  <DI, ES>
  873.                PUSHF                   ; **2
  874.                MOV   AL,-1             ; invalidar contenido buffer
  875.                JC    acceso_anota      ; si hay error
  876.                CMP   orden,F_VERIFY
  877.                JE    acceso_rep        ; nada leído físicamente
  878.                MOV   AL,unidad
  879. acceso_anota:  MOV   buf_unidad,AL
  880.                MOV   AL,cilindro
  881.                MOV   AH,cabezal
  882.                MOV   buf_cilcab,AX
  883.                MOV   AL,sector
  884.                MOV   buf_sector,AL     ; anotado el sector en buffer
  885. acceso_rep:    POPF                    ; ** mucho cuidado con la pila
  886.                CALL  set_err           ; ajustar variable «status»
  887. acceso_fin:    XPOP  <BX, AX>
  888.                RET
  889. acceso_sector  ENDP
  890.  
  891. ; ------------ Devolver el número de secciones del sector en curso.
  892.  
  893. num_secciones  PROC
  894.                CALL  pista0?
  895.                MOV   AL,1
  896.                JZ    num_secc_ok       ; sectores 512 en cil./cab. 0
  897.                XPUSH <BX, CX>
  898.                LEA   BX,[SI].tabla_tsect
  899.                ADD   BL,sector
  900.                ADC   BH,0
  901.                MOV   CL,[BX-1]
  902.                SUB   CL,2
  903.                MOV   AL,1
  904.                SHL   AL,CL
  905.                XPOP  <CX, BX>
  906. num_secc_ok:   RET                     ; resultado en AL
  907. num_secciones  ENDP
  908.  
  909. ; ------------ Asegurar que el motor está en marcha.
  910.  
  911. motor_ok       PROC
  912.                PUSHA                   ; *
  913.                PUSH  DS                ; **
  914.                MOV   BX,40h
  915.                PUSH  BX
  916.                POP   DS
  917.                MOV   CH,255-18         ; CH = 255 - 1 segundo
  918.                CLI
  919.                MOV   CL,CS:unidad
  920.                MOV   AL,1
  921.                SHL   AL,CL
  922.                TEST  [BX-1],AL         ; ¿motor en marcha?
  923.                JZ    arrancarlo        ; arrancarlo
  924.                CMP   [BX],CH           ; Si encendido y acelerado...
  925.                JBE   ok_motor          ; ...seguir
  926. arrancarlo:    OR    [BX-1],AL         ; arrancar motor
  927.                AND   BYTE PTR [BX-1],0CFh  ; borrar número unidad
  928.                MOV   AL,CL
  929.                SHL   AL,4              ; unidad << 4
  930.                OR    [BX-1],AL         ; nuevo número de unidad
  931.                MOV   BYTE PTR [BX],255 ; asegurar que no se pare
  932.                STI
  933.                MOV   DX,3F2h           ; registro de salida digital
  934.                ADD   CL,4
  935.                MOV   AL,1
  936.                SHL   AL,CL             ; colocar bit del motor
  937.                OR    AL,CS:unidad      ; seleccionar unidad
  938.                OR    AL,00001100b      ; modo DMA, no hacer reset
  939.                OUT   DX,AL             ; poner en marcha el motor
  940.                MOV   AX,90FDh
  941.                CLC
  942.                INT   15h               ; permitir multitarea
  943.                JC    ok_motor          ; timeout
  944.                MOV   AX,1000           ; 1 segundo aceleración
  945.                CALL  retardo           ; esperar aceleración disco
  946. ok_motor:      MOV   [BX],CH           ; cuenta máxima detención motor
  947.                STI                     ; sin forzar futura aceleración
  948.                POP   DS                ; **
  949.                POPA                    ; *
  950.                RET
  951. motor_ok       ENDP
  952.  
  953. ; ------------ Establecer modalidad de operación del controlador
  954. ;              y poner el motor en marcha. Si CF=1 se le da tiempo
  955. ;              además a la unidad para que acelere.
  956.  
  957. reset_drv      PROC
  958.                PUSHA
  959.                CALL  motor_off_cnt     ; cuenta detención motor
  960.                MOV   CL,unidad
  961.                MOV   AL,CL
  962.                SHL   AL,2              ; unidad seleccionada
  963.                OR    AL,1              ; bit de motor
  964.                SHL   AL,CL             ; colocar dicho bit
  965.                PUSH  DS                ; *
  966.                PUSH  40h
  967.                POP   DS
  968.                CLI
  969.                MOV   DS:[3Fh],AL
  970.                AND   BYTE PTR DS:[3Eh],70h ; bit IRQ=0 y recalibrar
  971.                POP   DS                ; *
  972.                SHL   AL,4              ; bits motor en nibble alto
  973.                OR    AL,CL             ; seleccionar unidad
  974.                OR    AL,00001000b      ; interrupciones+DMA y reset
  975.                MOV   DX,3F2h           ; registro de salida digital
  976.                OUT   DX,AL             ; señal de reset
  977.                CALL  fdc_respiro       ; tiempo reconocer reset en 486
  978.                OR    AL,00000100b
  979.                OUT   DX,AL             ; fin de señal de reset
  980.                CALL  espera_int        ; rehabilitará interrupciones
  981.                MOV   AL,8
  982.                CALL  fdc_write         ; comando 'leer estado int...'
  983.                CALL  fdc_read
  984.                CALL  fdc_read
  985.                CALL  envia_specify     ; comando 'specify' adecuado
  986.                POPA
  987.                RET
  988. reset_drv      ENDP
  989.  
  990. ; ------------ Enviar comando specify a la controladora. El step-rate
  991. ;              se selecciona según la densidad, para evitar un sonido
  992. ;              extraño al posicionar o recalibrar el cabezal.
  993.  
  994. envia_specify  PROC
  995.                PUSH  AX
  996.                PUSH  DS
  997.                PUSH  40h
  998.                POP   DS
  999.                MOV   AH,DS:[8Bh]
  1000.                POP   DS
  1001.                MOV   AL,3              ; comando 'specify'
  1002.                CALL  fdc_write
  1003.                MOV   AL,0BFh           ; step rate para 500 kbps
  1004.                AND   AH,11000000b
  1005.                JZ    spec1_ok
  1006.                MOV   AL,0AFh           ; step rate para 1 Mbps
  1007.                CMP   AH,11000000b
  1008.                JE    spec1_ok
  1009.                MOV   AL,0DFh           ; step rate para 250/300 Kbps
  1010. spec1_ok:      CALL  fdc_write
  1011.                MOV   AL,2
  1012.                CALL  fdc_write         ; head load y modo DMA
  1013.                POP   AX
  1014.                RET
  1015. envia_specify  ENDP
  1016.  
  1017. ; ------------ Recargar cuenta para la detención del motor. Si CF=1 al
  1018. ;              entrar, se establece la mayor cuenta posible; en caso
  1019. ;              contrario, se pone el valor normal de la tabla base.
  1020.  
  1021. motor_off_cnt  PROC
  1022.                PUSHA
  1023.                PUSH  DS
  1024.                MOV   AL,0FFh           ; valor máximo
  1025.                JC    motor_off_ok
  1026.                PUSH  0
  1027.                POP   DS
  1028.                LDS   BX,DWORD PTR DS:[1Eh*4] ; DS:BX -> INT 1Eh
  1029.                MOV   AL,[BX+2]               ; byte 2 tabla base disco
  1030. motor_off_ok:  PUSH  40h
  1031.                POP   DS
  1032.                MOV   BYTE PTR DS:[40h],AL  ; cuenta parada motor
  1033.                POP   DS
  1034.                POPA
  1035.                RET
  1036. motor_off_cnt  ENDP
  1037.  
  1038. ; ------------ Llevar el cabezal a la pista indicada, recalibrando si
  1039. ;              hubo un reset (se invocó la función 0 de la INT 13h o
  1040. ;              se ejecutó reset_drv) antes de esta operación. Primero
  1041. ;              se selecciona la velocidad de transferencia y se borra
  1042. ;              el resultado de cualquier operación anterior, para que
  1043. ;              todo quede listo para el próximo acceso a disco.
  1044.  
  1045. seek_drv       PROC
  1046.                PUSHA
  1047.                CALL  set_rate          ; velocidad / borrar resultados
  1048.                CALL  envia_specify     ; comando 'specify' adecuado
  1049.                MOV   AH,1
  1050.                MOV   CL,unidad
  1051.                SHL   AH,CL             ; AH = 1 (A:) ó 2 (B:)
  1052.                PUSH  DS
  1053.                PUSH  40h
  1054.                POP   DS
  1055.                TEST  AH,DS:[3Eh]
  1056.                POP   DS
  1057.                JNZ   do_seek           ; la unidad ya fue recalibrada
  1058.                CALL  recalibrar
  1059.                JC    fallo_seek        ; fallo al recalibrar
  1060. do_seek:       MOV   BX,94h
  1061.                ADD   BL,unidad
  1062.                MOV   AL,cilindro
  1063.                PUSH  DS                ; *
  1064.                PUSH  40h
  1065.                POP   DS
  1066.                OR    DS:[3Eh],AH       ; unidad ya recalibrada
  1067.                MOV   AH,DS:[41h]       ; código de error previo
  1068.                CMP   AL,[BX]
  1069.                MOV   [BX],AL
  1070.                POP   DS                ; *
  1071.                JNE   hacer_seek        ; seek necesario
  1072.                CMP   AH,40h            ; ¿error de seek previo?
  1073.                JNE   seek_ok           ; no, evitar seek innecesario
  1074. hacer_seek:    MOV   AL,0Fh
  1075.                CALL  fdc_write         ; comando 'seek'
  1076.                JC    fallo_seek
  1077.                MOV   AL,cabezal
  1078.                SHL   AL,2
  1079.                OR    AL,unidad
  1080.                CALL  fdc_write         ; enviar HD, US1, US0
  1081.                MOV   AL,cilindro
  1082.                CALL  fdc_write         ; enviar cilindro
  1083.                CALL  espera_int        ; esperar interrupción
  1084.                JC    fallo_seek
  1085.                MOV   AL,8
  1086.                CALL  fdc_write         ; comando 'leer estado int...'
  1087.                JC    fallo_seek
  1088.                CALL  fdc_read          ; leer registro de estado 0
  1089.                JC    fallo_seek
  1090.                MOV   AH,AL
  1091.                CALL  fdc_read          ; leer cilindro actual
  1092.                TEST  AH,11000000b      ; comprobar ST0
  1093.                JNZ   fallo_seek
  1094.                MOV   AL,15             ; estabilización para escritura
  1095.                CMP   orden,F_WRITE
  1096.                JE    rseek_ok
  1097.                MOV   AL,1              ; estabilización para lectura
  1098. rseek_ok:      CBW                     ; AH = 0
  1099.                CALL  retardo           ; esperar asentamiento cabezal
  1100. seek_ok:       POPA
  1101.                CLC                     ; retornar con éxito
  1102.                RET
  1103. fallo_seek:    POPA
  1104.                STC                     ; retornar indicando fallo
  1105.                RET
  1106. seek_drv       ENDP
  1107.  
  1108. ; ------------ Establecer velocidad de transferencia correcta si aún
  1109. ;              no ha sido seleccionada y borrar el resultado de otra
  1110. ;              operación previa.
  1111.  
  1112. set_rate       PROC
  1113.                PUSHA
  1114.                CALL  pista0?
  1115.                MOV   AX,[SI].vunidad   ; velocidad pista 0 / demás
  1116.                JZ    vel_ok
  1117.                MOV   AL,AH
  1118. vel_ok:        PUSH  DS                ; *
  1119.                PUSH  40h
  1120.                POP   DS
  1121.                MOV   AH,DS:[8Bh]
  1122.                SHR   AH,6              ; aislar bits de velocidad
  1123.                CMP   AL,AH
  1124.                JE    vel_set           ; velocidad ya seleccionada
  1125.                MOV   DX,3F7h
  1126.                OUT   DX,AL             ; seleccionarla
  1127.                SHL   AL,6
  1128.                AND   BYTE PTR DS:[8Bh],00111111b
  1129.                OR    DS:[8Bh],AL
  1130. vel_set:       POP   DS                ; *
  1131.                LEA   DI,status
  1132.                MOV   CX,8
  1133. borra_status:  MOV   [DI],CH           ; borrar información de estado
  1134.                INC   DI
  1135.                LOOP  borra_status
  1136.                POPA
  1137.                RET
  1138. set_rate       ENDP
  1139.  
  1140. ; ------------ Recalibrar la unidad (si hay error se intenta otra vez
  1141. ;              para el caso de que deba moverse más de 77 pistas).
  1142.  
  1143. recalibrar     PROC
  1144.                PUSHA
  1145.                MOV   BX,94h
  1146.                ADD   BL,unidad
  1147.                PUSH  DS                ; *
  1148.                PUSH  40h
  1149.                POP   DS
  1150.                MOV   [BX],BH           ; pista actual = 0
  1151.                POP   DS                ; *
  1152.                MOV   CX,2              ; dos veces como mucho
  1153. recalibra:     MOV   AL,7
  1154.                CALL  fdc_write         ; comando de 'recalibrado'
  1155.                JC    fallo_recal
  1156.                MOV   AL,cabezal
  1157.                SHL   AL,2
  1158.                OR    AL,unidad
  1159.                CALL  fdc_write         ; enviar HD, US1, US0
  1160.                JC    fallo_recal
  1161.                CALL  espera_int        ; esperar interrupción
  1162.                JC    fallo_recal
  1163.                MOV   AL,8
  1164.                CALL  fdc_write         ; comando 'leer estado int...'
  1165.                JC    fallo_recal
  1166.                CALL  fdc_read          ; leer registro de estado 0
  1167.                JC    fallo_recal
  1168.                MOV   AH,AL
  1169.                CALL  fdc_read          ; leer cilindro actual
  1170.                XOR   AH,00100000b      ; bajar bit de 'seek end'
  1171.                TEST  AH,11110000b      ; comprobar resultado y ST0
  1172.                JNZ   fallo_recal       ; sin 'seek end' o TRK0
  1173.                MOV   AX,1              ; pausa de 1 ms
  1174.                CALL  retardo
  1175.                JMP   recal_ret
  1176. fallo_recal:   LOOP  recalibra         ; reintentar comando
  1177.                STC                     ; condición de fallo
  1178. recal_ret:     POPA
  1179.                RET
  1180. recalibrar     ENDP
  1181.  
  1182. ; ------------ Cargar o escribir sector(es) del disco en ES:DI,
  1183. ;              actualizando la dirección en ES:DI pero sin alterar
  1184. ;              ningún otro registro. Si hay error se devuelve CF=1 y
  1185. ;              no se modifica ES:DI. A partir de fdc_result se dejan
  1186. ;              los 7 bytes que devuelve el FDC al final del acceso.
  1187. ;              En caso de verificación (F_VERIFY) se programa el DMA
  1188. ;              para que no realice transferencia física (convenio de
  1189. ;              las BIOS con fecha 15/11/85 y posterior).
  1190.  
  1191. sector_io      PROC
  1192.                XPUSH <AX, BX, CX, DX>
  1193.                MOV   CL,tsector
  1194.                MOV   CH,0
  1195.                STC
  1196.                RCL   CH,CL
  1197.                MOV   CL,0              ; nº de bytes por sector
  1198.                MOV   AL,sector_fin
  1199.                SUB   AL,sector_ini
  1200.                INC   AX
  1201.                CBW                     ; AX sectores (AH = 0)
  1202.                MUL   CX
  1203.                MOV   DX,AX             ; bytes totales
  1204.                MOV   CX,AX
  1205.                DEC   CX                ; bytes totales - 1
  1206.                MOV   AX,ES
  1207.                CALL  calc_dir_DMA      ; AX:DI -> base BX y página AH
  1208.                JC    sector_io_ko
  1209.                MOV   AL,orden          ; modo DMA necesario
  1210.                CALL  prepara_DMA
  1211.                CMP   AL,F_WRITE
  1212.                MOV   AL,11000101b      ; comando de escritura del FDC
  1213.                JE    orden_io_ok
  1214.                MOV   AL,11100110b      ; comando leer (verif.) del FDC
  1215. orden_io_ok:   CALL  fdc_write         ; comando leer/escribir del FDC
  1216.                JC    sector_io_ko
  1217.                MOV   AL,cabezal
  1218.                SHL   AL,2
  1219.                OR    AL,unidad
  1220.                CALL  fdc_write         ; byte 1 de la orden
  1221.                MOV   AL,cilindro
  1222.                CALL  fdc_write         ; enviar cilindro
  1223.                MOV   AL,cabezal
  1224.                CALL  fdc_write         ; enviar cabezal
  1225.                MOV   AL,sector_ini
  1226.                CALL  fdc_write         ; enviar nº sector
  1227.                MOV   AL,tsector
  1228.                CALL  fdc_write         ; longitud sector
  1229.                MOV   AL,sector_fin
  1230.                CALL  fdc_write         ; último sector
  1231.                MOV   AL,[SI].gap
  1232.                CALL  fdc_write         ; GAP de lectura/escritura
  1233.                MOV   AL,128
  1234.                CALL  fdc_write         ; tamaño sector si longitud=0
  1235.                CALL  espera_int
  1236.                PUSHF                   ; *
  1237.                LEA   BX,fdc_result
  1238.                MOV   CX,7
  1239. sect_io_res:   CALL  fdc_read          ; leyendo resultados
  1240.                MOV   [BX],AL
  1241.                INC   BX
  1242.                LOOP  sect_io_res
  1243.                POPF                    ; *
  1244.                JC    sector_io_ko
  1245.                TEST  fdc_result,11000000b
  1246.                JNZ   sector_io_ko
  1247.                ADD   DI,DX             ; actualizar dirección
  1248.                CLC                     ; Ok
  1249.                JMP   sector_io_fin
  1250. sector_io_ko:  STC                     ; indicar fallo
  1251. sector_io_fin: XPOP  <DX, CX, BX, AX>
  1252.                RET
  1253. sector_io      ENDP
  1254.  
  1255. ; ------------ Devolver en AH la página de DMA y en BX la base. A la
  1256. ;              entrada, AX:DI -> dirección de memoria y CX = bytes-1.
  1257. ;              Si se cruza una frontera de DMA se devuelve el error.
  1258.  
  1259. calc_dir_DMA   PROC
  1260.                PUSH  DX
  1261.                MOV   BX,16
  1262.                MUL   BX
  1263.                ADD   AX,DI
  1264.                ADC   DX,0              ; DX:AX = dirección 20 bits
  1265.                MOV   BX,AX             ; base en BX
  1266.                MOV   AH,DL             ; página
  1267.                MOV   DX,CX
  1268.                ADD   DX,BX
  1269.                JNC   dir_DMA_ok
  1270.                MOV   status,9          ; error de frontera de DMA
  1271. dir_DMA_ok:    POP   DX
  1272.                RET
  1273. calc_dir_DMA   ENDP
  1274.  
  1275. ; ------------ Esperar interrupción de disquete durante casi 2
  1276. ;              segundos antes de considerar que ha sido un fracaso.
  1277.  
  1278. espera_int     PROC
  1279.                STI
  1280.                PUSHA
  1281.                XPUSH <DS, 40h>
  1282.                POP   DS
  1283.                MOV   AX,9001h
  1284.                CLC
  1285.                INT   15h               ; permitir multitarea
  1286.                MOV   DX,0280h
  1287.                MOV   BX,3Eh
  1288.                JC    timeout_int
  1289. esp_int_1s:    XOR   CX,CX
  1290. esp_int:       TEST  [BX],DL           ; ¿llegó la interrupción?
  1291.                JNZ   fin_espera
  1292.                PMICRO
  1293.                LOOP  esp_int           ; esperar durante casi 1 seg.
  1294.                DEC   DH
  1295.                JNZ   esp_int_1s
  1296. timeout_int:   OR    CS:status,DL      ; timeout
  1297.                STC
  1298. fin_espera:    PUSHF
  1299.                AND   BYTE PTR [BX],7Fh ; para la próxima vez
  1300.                POPF
  1301.                POP   DS
  1302.                POPA
  1303.                RET
  1304. espera_int     ENDP
  1305.  
  1306. ; ------------ Preparar DMA para E/S. A la entrada, BX = dirección de
  1307. ;              base, AH = registro de página y CX = nº bytes - 1.
  1308.  
  1309. prepara_DMA    PROC
  1310.                PUSH  AX
  1311.                CLI
  1312.                OUT   0Bh,AL            ; registro de modo del DMA
  1313.                MOV   AL,0
  1314.                DELAY
  1315.                OUT   0Ch,AL            ; clear first/last flip-flop
  1316.                MOV   AL,BL
  1317.                DELAY
  1318.                OUT   4,AL
  1319.                MOV   AL,BH
  1320.                DELAY
  1321.                OUT   4,AL              ; enviada dirección base
  1322.                DELAY
  1323.                MOV   AL,AH
  1324.                OUT   81h,AL            ; registro de página del DMA
  1325.                MOV   AL,CL
  1326.                DELAY
  1327.                OUT   5,AL
  1328.                MOV   AL,CH
  1329.                DELAY
  1330.                OUT   5,AL              ; enviada cuenta de bytes
  1331.                STI
  1332.                MOV   AL,2
  1333.                DELAY
  1334.                OUT   0Ah,AL            ; habilitar canal 2 de DMA
  1335.                POP   AX
  1336.                RET
  1337. prepara_DMA    ENDP
  1338.  
  1339. ; ------------ Recibir byte del FDC en AL. A la vuelta, CF=1 si
  1340. ;              la operación fracasó (el FDC no estaba listo) y
  1341. ;              se indica la condición de timeout en «status».
  1342.  
  1343. fdc_read       PROC
  1344.                XPUSH <CX, DX, AX>
  1345.                CALL  fdc_respiro       ; no abrasar el FDC
  1346.                MOV   DX,3F4h           ; registro de estado del FDC
  1347.                MOV   CX,133            ; constante para 0,002 segundos
  1348. espera_rd:     DELAY
  1349.                IN    AL,DX
  1350.                AND   AL,11000000b
  1351.                CMP   AL,11000000b      ; ¿dato listo?
  1352.                JE    fdc_rd_ok
  1353.                DELAY
  1354.                IN    AL,61h
  1355.                AND   AL,10h
  1356.                CMP   AL,AH
  1357.                JE    espera_rd         ; reintentarlo durante 15,09 µs
  1358.                MOV   AH,AL
  1359.                LOOP  espera_rd
  1360.                XPOP  <AX, DX, CX>
  1361.                OR    status,80h        ; timeout
  1362.                MOV   AL,0
  1363.                STC                     ; fallo
  1364.                RET
  1365. fdc_rd_ok:     POP   AX
  1366.                INC   DX                ; apuntar al registro de datos
  1367.                DELAY
  1368.                IN    AL,DX             ; leer byte del FDC
  1369.                XPOP  <DX, CX>
  1370.                CLC                     ; Ok
  1371.                RET
  1372. fdc_read       ENDP
  1373.  
  1374. ; ------------ Enviar byte AL al FDC. A la vuelta, CF=1 si
  1375. ;              la operación fracasó (el FDC no estaba listo) y
  1376. ;              se indica la condición de timeout en «status».
  1377.  
  1378. fdc_write      PROC
  1379.                XPUSH <CX, DX, AX>
  1380.                CALL  fdc_respiro       ; no abrasar el FDC
  1381.                MOV   DX,3F4h           ; registro de estado del FDC
  1382.                MOV   CX,133            ; constante para 0,002 segundos
  1383. espera_wr:     DELAY
  1384.                IN    AL,DX
  1385.                TEST  AL,80h            ; ¿listo para E/S?
  1386.                JNZ   fdc_wr_ok
  1387.                DELAY
  1388.                IN    AL,61h
  1389.                AND   AL,10h
  1390.                CMP   AL,AH
  1391.                JE    espera_wr         ; reintentarlo durante 15,09 µs
  1392.                MOV   AH,AL
  1393.                LOOP  espera_wr
  1394.                XPOP  <AX, DX, CX>
  1395.                OR    status,80h        ; timeout
  1396.                STC                     ; fallo
  1397.                RET
  1398. fdc_wr_ok:     INC   DX                ; apuntar al registro de datos
  1399.                POP   AX
  1400.                DELAY
  1401.                OUT   DX,AL             ; enviar byte al FDC
  1402.                XPOP  <DX, CX>
  1403.                CLC                     ; Ok
  1404.                RET
  1405. fdc_write      ENDP
  1406.  
  1407. ; ------------ Retardo de 60 µs para dar tiempo al FDC en 486 rápidos.
  1408.  
  1409. fdc_respiro    PROC
  1410.                XPUSH <AX, CX>
  1411.                MOV   CX,4
  1412. fdc_ret:       PMICRO
  1413.                LOOP  fdc_ret
  1414.                XPOP  <CX, AX>
  1415.                RET
  1416. fdc_respiro    ENDP
  1417.  
  1418. ; ------------ Esperar exactamente AX milisegundos.
  1419.  
  1420. retardo        PROC
  1421.                PUSHF
  1422.                PUSHA
  1423.                MOV   DX,16970          ; 16970 = 1193180/18*256/1000
  1424.                MUL   DX
  1425.                MOV   CL,AH             ; dividir DX:AX entre 256 y
  1426.                MOV   CH,DL             ; dejar el resultado en DX:CX
  1427.                MOV   DL,DH
  1428.                MOV   DH,0              ; DX:CX 15,09 µs-avos
  1429. retardando:    PMICRO
  1430.                LOOP  retardando
  1431.                AND   DX,DX
  1432.                JZ    retardado
  1433.                DEC   DX
  1434.                JMP   retardando
  1435. retardado:     POPA
  1436.                POPF
  1437.                RET
  1438. retardo        ENDP
  1439.  
  1440. ; ------------ Esta subrutina sustituye a la macro PMICRO del programa
  1441. ;              completo por razones de espacio.
  1442.  
  1443. pmicro_iter:   DELAY                   ; retardo de aprox. 15,09 µs
  1444.                IN    AL,61h            ; (exactamente 18/1193180 sg.)
  1445.                AND   AL,10h            ; La rutina se puede ejecutar
  1446.                CMP   AL,AH             ; repetitivamente (se apoya en
  1447.                JE    pmicro_iter       ; AX) para hacer retardos a
  1448.                MOV   AH,AL             ; través de la temporización
  1449.                RET                     ; del refresco de la memoria
  1450.  
  1451. ; ------------ Código invocado durante el SuperBOOT desde 2MFKIT.ASM
  1452. ;              A la entrada: CS=ES, SS=0 y AX = tipo unidades.
  1453.  
  1454. initcode:      PUSH  DS
  1455.                PUSH  SS
  1456.                POP   DS
  1457.                MOV   ES:[info_A.tipo_drv],AL  ; anotar tipo de A:
  1458.                MOV   ES:[info_B.tipo_drv],AH  ; anotar tipo de B:
  1459.                LEA   DI,ant_int13
  1460.                MOV   SI,13h*4              ; vector de INT 13h
  1461.                CLD
  1462.                CLI
  1463.                MOVSW
  1464.                MOVSW                       ; anotada dirección INT 13h
  1465.                MOV   WORD PTR [SI-4],OFFSET ges_int13
  1466.                MOV   [SI-2],ES
  1467.                STI                         ; desviada INT 13h
  1468.                POP   DS
  1469.                RETF                        ; volver a 2MFKIT
  1470.  
  1471.                DB    5 DUP (0)    ; para completar 2560 bytes exactos
  1472.  
  1473. ant_int13      LABEL DWORD        ; vector de la INT 13h previa
  1474. ant_int13_off  DW    initcode
  1475. ant_int13_seg  DW    0AA55h       ; significa "2MFBOOT correcto"
  1476.  
  1477.                ; --- Ubicación del sector de hasta 2048 bytes.
  1478.                ;     Por su peculiar ubicación en la memoria (al final
  1479.                ;     de los 640K, 512K, etc, pero sin llegar) nunca
  1480.                ;     cruzará una frontera de DMA...
  1481.  
  1482. buffer_io      EQU   $          ; obviamente ¡no se almacena!
  1483.  
  1484. ON             EQU   1          ; constantes booleanas
  1485. OFF            EQU   0
  1486. F_READ         EQU   46h        ; modo DMA para lectura
  1487. F_WRITE        EQU   4Ah        ; modo DMA para escritura
  1488. F_VERIFY       EQU   42h        ; modo DMA para verificación
  1489.  
  1490. _PRINCIPAL     ENDS
  1491.                END
  1492.